package nachos.userprog;

import nachos.machine.*;
import nachos.threads.*;

import java.util.LinkedList;

/**
 * A kernel that can support multiple user processes.
 */
public class UserKernel extends ThreadedKernel
{
    /**
     * Allocate a new user kernel.
     */
    public UserKernel()
    {
        super();
    }

    public static Lock getAllocateMemoryLock()
    {
        return allocateMemoryLock;
    }

    /**
     * Initialize this kernel. Creates a synchronized console and sets the
     * processor's exception handler.
     */
    public void initialize(String[] args)
    {
        super.initialize(args);

        console = new SynchConsole(Machine.console());

        allocateMemoryLock = new Lock();
        //初始化的时候，使memoryLinkedList包含所有的页号
        freePages = new LinkedList<TranslationEntry>();
        for (int currentPageIndex = 0; currentPageIndex < Machine.processor().getNumPhysPages(); currentPageIndex++)
            freePages.add(new TranslationEntry(0, currentPageIndex, false, false, false, false));

        Machine.processor().setExceptionHandler(new Runnable()
        {
            public void run()
            {
                exceptionHandler();
            }
        });
    }

    /**
     * Test the console device.
     */
    public void selfTest()
    {
        super.selfTest();

//        System.out.println("Testing the console device. Typed characters");
//        System.out.println("will be echoed until q is typed.");
//
//        char c;
//
//        do
//        {
//            c = (char) console.readByte(true);
//            console.writeByte(c);
//        }
//        while (c != 'q');
//
//        System.out.println("");
    }

    /**
     * Returns the current process.
     *
     * @return the current process, or <tt>null</tt> if no process is current.
     */
    public static UserProcess currentProcess()
    {
        if (!(KThread.currentThread() instanceof UThread))
            return null;

        return ((UThread) KThread.currentThread()).process;
    }

    /**
     * The exception handler. This handler is called by the processor whenever
     * a user instruction causes a processor exception.
     *
     * <p>
     * When the exception handler is invoked, interrupts are enabled, and the
     * processor's cause register contains an integer identifying the cause of
     * the exception (see the <tt>exceptionZZZ</tt> constants in the
     * <tt>Processor</tt> class). If the exception involves a bad virtual
     * address (e.g. page fault, TLB miss, read-only, bus error, or address
     * error), the processor's BadVAddr register identifies the virtual address
     * that caused the exception.
     */
    public void exceptionHandler()
    {
        Lib.assertTrue(KThread.currentThread() instanceof UThread);

        UserProcess process = ((UThread) KThread.currentThread()).process;
        int cause = Machine.processor().readRegister(Processor.regCause);
        process.handleException(cause);
    }

    /**
     * Start running user programs, by creating a process and running a shell
     * program in it. The name of the shell program it must run is returned by
     * <tt>Machine.getShellProgramName()</tt>.
     *
     * @see    Machine#getShellProgramName
     */
    public void run()
    {
        super.run();
        UserProcess process = UserProcess.newUserProcess();

        String shellProgram = Machine.getShellProgramName();
        Lib.assertTrue(process.execute(shellProgram, new String[]{}));

        KThread.currentThread().finish();
    }

    /**
     * Terminate this kernel. Never returns.
     */
    public void terminate()
    {
        super.terminate();
    }

    /**
     * Acquire the requested number of pages from the free pages in <tt>this</tt>.
     * @param numPages
     * @return
     */
    public static TranslationEntry[] acquirePages(int numPages){
        TranslationEntry[] returnPages = null;

        allocateMemoryLock.acquire();

        if (!freePages.isEmpty() && freePages.size() >= numPages) {
            returnPages = new TranslationEntry[numPages];

            for (int i = 0; i < numPages; ++i) {
                returnPages[i] = freePages.remove();
                returnPages[i].valid = true;
            }
        }

        allocateMemoryLock.release();

        return returnPages;
    }

    /**
     * Return the pages in <tt>pageTable</tt> to the free pages in <tt>this</tt>.
     *
     * This should only be called by a dying (i.e. completed) <tt>UserProcess</tt> instance.
     * @param pageTable
     */
    public static void releasePages(TranslationEntry[] pageTable) {
        allocateMemoryLock.acquire();

        for (TranslationEntry te : pageTable) {
            freePages.add(te);
            te.valid = false;
        }

        allocateMemoryLock.release();
    }

    /**
     * Globally accessible reference to the synchronized console.
     */
    public static SynchConsole console;

    // dummy variables to make javac smarter
    private static Coff dummy1 = null;

    //保护内存互斥访问锁
    private static Lock allocateMemoryLock;
    //全局队列，存放当前空闲的物理页号
    private static LinkedList<TranslationEntry> freePages;

    public static LinkedList<TranslationEntry> getFreePages()
    {
        return freePages;
    }


}
